1   /*
2    * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   */
23  
24  /*
25   *
26   * @test
27   * @bug 4533872 4915683 4985217 5017280 6937112
28   * @summary Unit tests for supplementary character support (JSR-204)
29   */
30  
31  public class Supplementary {
32  
33      public static void main(String[] args) {
34          test1();        // Test for codePointAt(int index)
35          test2();        // Test for codePointBefore(int index)
36          test3();        // Test for reverse()
37          test4();        // Test for appendCodePoint(int codePoint)
38          test5();        // Test for codePointCount(int beginIndex, int endIndex)
39          test6();        // Test for offsetByCodePoints(int index, int offset)
40      }
41  
42      /* Text strings which are used as input data.
43       * The comment above each text string means the index of each 16-bit char
44       * for convenience.
45       */
46      static final String[] input = {
47        /*                               111     1     111111     22222
48           0123     4     5678     9     012     3     456789     01234 */
49          "abc\uD800\uDC00def\uD800\uD800ab\uD800\uDC00cdefa\uDC00bcdef",
50        /*                          1     1111     1111     1     222
51           0     12345     6789     0     1234     5678     9     012     */
52          "\uD800defg\uD800hij\uD800\uDC00klm\uDC00nop\uDC00\uD800rt\uDC00",
53        /*                          11     1     1111     1     112     222
54           0     12345     6     78901     2     3456     7     890     123     */
55          "\uDC00abcd\uDBFF\uDFFFefgh\uD800\uDC009ik\uDC00\uDC00lm\uDC00no\uD800",
56        /*                                    111     111111     1 22     2
57           0     1     2345     678     9     012     345678     9 01     2     */
58          "\uD800\uDC00!#$\uD800%&\uD800\uDC00;+\uDC00<>;=^\uDC00\\@\uD800\uDC00",
59  
60          // includes an undefined supplementary character in Unicode 4.0.0
61        /*                                    1     11     1     1111     1
62           0     1     2345     6     789     0     12     3     4567     8     */
63          "\uDB40\uDE00abc\uDE01\uDB40de\uDB40\uDE02f\uDB40\uDE03ghi\uDB40\uDE02",
64      };
65  
66  
67      /* Expected results for:
68       *     test1(): for codePointAt()
69       *
70       * Each character in each array is the golden data for each text string
71       * in the above input data. For example, the first data in each array is
72       * for the first input string.
73       */
74      static final int[][] golden1 = {
75          {'a',    0xD800, 0xDC00,  0x10000, 0xE0200}, // codePointAt(0)
76          {0xD800, 0x10000, 'g',    0xDC00,  0xE0202}, // codePointAt(9)
77          {'f',    0xDC00,  0xD800, 0xDC00,  0xDE02},  // codePointAt(length-1)
78      };
79  
80      /*
81       * Test for codePointAt(int index) method
82       */
83      static void test1() {
84  
85          for (int i = 0; i < input.length; i++) {
86              StringBuffer sb = new StringBuffer(input[i]);
87  
88              /*
89               * Normal case
90               */
91              testCodePoint(At, sb, 0, golden1[0][i]);
92              testCodePoint(At, sb, 9, golden1[1][i]);
93              testCodePoint(At, sb, sb.length()-1, golden1[2][i]);
94  
95              /*
96               * Abnormal case - verify that an exception is thrown.
97               */
98              testCodePoint(At, sb, -1);
99              testCodePoint(At, sb, sb.length());
100         }
101     }
102 
103 
104     /* Expected results for:
105      *     test2(): for codePointBefore()
106      *
107      * Each character in each array is the golden data for each text string
108      * in the above input data. For example, the first data in each array is
109      * for the first input string.
110      */
111     static final int[][] golden2 = {
112         {'a',    0xD800, 0xDC00,  0xD800,  0xDB40},  // codePointBefore(1)
113         {0xD800, 'l',    0x10000, 0xDC00,  0xDB40},  // codePointBefore(13)
114         {'f',    0xDC00, 0xD800,  0x10000, 0xE0202}, // codePointBefore(length)
115     };
116 
117     /*
118      * Test for codePointBefore(int index) method
119      */
120     static void test2() {
121 
122         for (int i = 0; i < input.length; i++) {
123             StringBuffer sb = new StringBuffer(input[i]);
124 
125             /*
126              * Normal case
127              */
128             testCodePoint(Before, sb, 1, golden2[0][i]);
129             testCodePoint(Before, sb, 13, golden2[1][i]);
130             testCodePoint(Before, sb, sb.length(), golden2[2][i]);
131 
132             /*
133              * Abnormal case - verify that an exception is thrown.
134              */
135             testCodePoint(Before, sb, 0);
136             testCodePoint(Before, sb, sb.length()+1);
137         }
138     }
139 
140 
141     /* Expected results for:
142      *     test3(): for reverse()
143      *
144      * Unlike golden1 and golden2, each array is the golden data for each text
145      * string in the above input data. For example, the first array is  for
146      * the first input string.
147      */
148     static final String[] golden3 = {
149         "fedcb\uDC00afedc\uD800\uDC00ba\uD800\uD800fed\uD800\uDC00cba",
150         "\uDC00tr\uD800\uDC00pon\uDC00mlk\uD800\uDC00jih\uD800gfed\uD800",
151         "\uD800on\uDC00ml\uDC00\uDC00ki9\uD800\uDC00hgfe\uDBFF\uDFFFdcba\uDC00",
152         "\uD800\uDC00@\\\uDC00^=;><\uDC00+;\uD800\uDC00&%\uD800$#!\uD800\uDC00",
153 
154         // includes an undefined supplementary character in Unicode 4.0.0
155         "\uDB40\uDE02ihg\uDB40\uDE03f\uDB40\uDE02ed\uDB40\uDE01cba\uDB40\uDE00",
156     };
157 
158     // Additional input data & expected result for test3()
159     static final String[][] testdata1 = {
160         {"a\uD800\uDC00", "\uD800\uDC00a"},
161         {"a\uDC00\uD800", "\uD800\uDC00a"},
162         {"\uD800\uDC00a", "a\uD800\uDC00"},
163         {"\uDC00\uD800a", "a\uD800\uDC00"},
164         {"\uDC00\uD800\uD801", "\uD801\uD800\uDC00"},
165         {"\uDC00\uD800\uDC01", "\uD800\uDC01\uDC00"},
166         {"\uD801\uD800\uDC00", "\uD800\uDC00\uD801"},
167         {"\uD800\uDC01\uDC00", "\uDC00\uD800\uDC01"},
168         {"\uD800\uDC00\uDC01\uD801", "\uD801\uDC01\uD800\uDC00"},
169     };
170 
171     /*
172      * Test for reverse() method
173      */
174     static void test3() {
175         for (int i = 0; i < input.length; i++) {
176             StringBuffer sb = new StringBuffer(input[i]).reverse();
177 
178             check(!golden3[i].equals(new String(sb)),
179                  "reverse() for <" + toHexString(input[i]) + ">",
180                  sb, golden3[i]);
181         }
182 
183         for (int i = 0; i < testdata1.length; i++) {
184             StringBuffer sb = new StringBuffer(testdata1[i][0]).reverse();
185 
186             check(!testdata1[i][1].equals(new String(sb)),
187                  "reverse() for <" + toHexString(testdata1[i][0]) + ">",
188                  sb, testdata1[i][1]);
189         }
190     }
191 
192     /**
193      * Test for appendCodePoint() method
194      */
195     static void test4() {
196         for (int i = 0; i < input.length; i++) {
197             String s = input[i];
198             StringBuffer sb = new StringBuffer();
199             int c;
200             for (int j = 0; j < s.length(); j += Character.charCount(c)) {
201                 c = s.codePointAt(j);
202                 StringBuffer rsb = sb.appendCodePoint(c);
203                 check(sb != rsb, "appendCodePoint returned a wrong object");
204                 int sbc = sb.codePointAt(j);
205                 check(sbc != c, "appendCodePoint(j) != c", sbc, c);
206             }
207             check(!s.equals(sb.toString()),
208                   "appendCodePoint() produced a wrong result with input["+i+"]");
209         }
210 
211         // test exception
212         testAppendCodePoint(-1, IllegalArgumentException.class);
213         testAppendCodePoint(Character.MAX_CODE_POINT+1, IllegalArgumentException.class);
214     }
215 
216     /**
217      * Test codePointCount(int, int)
218      *
219      * This test case assumes that
220      * Character.codePointCount(CharSequence, int, int) works
221      * correctly.
222      */
223     static void test5() {
224         for (int i = 0; i < input.length; i++) {
225             String s = input[i];
226             StringBuffer sb = new StringBuffer(s);
227             int length = sb.length();
228             for (int j = 0; j <= length; j++) {
229                 int result = sb.codePointCount(j, length);
230                 int expected = Character.codePointCount(sb, j, length);
231                 check(result != expected, "codePointCount(input["+i+"], "+j+", "+length+")",
232                       result, expected);
233             }
234             for (int j = length; j >= 0; j--) {
235                 int result = sb.codePointCount(0, j);
236                 int expected = Character.codePointCount(sb, 0, j);
237                 check(result != expected, "codePointCount(input["+i+"], 0, "+j+")",
238                       result, expected);
239             }
240 
241             // test exceptions
242             testCodePointCount(null, 0, 0, NullPointerException.class);
243             testCodePointCount(sb, -1, length, IndexOutOfBoundsException.class);
244             testCodePointCount(sb, 0, length+1, IndexOutOfBoundsException.class);
245             testCodePointCount(sb, length, length-1, IndexOutOfBoundsException.class);
246         }
247     }
248 
249     /**
250      * Test offsetByCodePoints(int, int)
251      *
252      * This test case assumes that
253      * Character.codePointCount(CharSequence, int, int) works
254      * correctly.
255      */
256     static void test6() {
257         for (int i = 0; i < input.length; i++) {
258             String s = input[i];
259             StringBuffer sb = new StringBuffer(s);
260             int length = s.length();
261             for (int j = 0; j <= length; j++) {
262                 int nCodePoints = Character.codePointCount(sb, j, length);
263                 int result = sb.offsetByCodePoints(j, nCodePoints);
264                 check(result != length,
265                       "offsetByCodePoints(input["+i+"], "+j+", "+nCodePoints+")",
266                       result, length);
267                 result = sb.offsetByCodePoints(length, -nCodePoints);
268                 int expected = j;
269                 if (j > 0 && j < length) {
270                     int cp = sb.codePointBefore(j+1);
271                     if (Character.isSupplementaryCodePoint(cp)) {
272                         expected--;
273                     }
274                 }
275                 check(result != expected,
276                       "offsetByCodePoints(input["+i+"], "+j+", "+(-nCodePoints)+")",
277                       result, expected);
278             }
279             for (int j = length; j >= 0; j--) {
280                 int nCodePoints = Character.codePointCount(sb, 0, j);
281                 int result = sb.offsetByCodePoints(0, nCodePoints);
282                 int expected = j;
283                 if (j > 0 && j < length) {
284                     int cp = sb.codePointAt(j-1);
285                      if (Character.isSupplementaryCodePoint(cp)) {
286                         expected++;
287                     }
288                 }
289                 check(result != expected,
290                       "offsetByCodePoints(input["+i+"], 0, "+nCodePoints+")",
291                       result, expected);
292                 result = sb.offsetByCodePoints(j, -nCodePoints);
293                 check(result != 0,
294                       "offsetBycodePoints(input["+i+"], "+j+", "+(-nCodePoints)+")",
295                       result, 0);
296             }
297 
298             // test exceptions
299             testOffsetByCodePoints(null, 0, 0, NullPointerException.class);
300             testOffsetByCodePoints(sb, -1, length, IndexOutOfBoundsException.class);
301             testOffsetByCodePoints(sb, 0, length+1, IndexOutOfBoundsException.class);
302             testOffsetByCodePoints(sb, 1, -2, IndexOutOfBoundsException.class);
303             testOffsetByCodePoints(sb, length, length-1, IndexOutOfBoundsException.class);
304             testOffsetByCodePoints(sb, length, -(length+1), IndexOutOfBoundsException.class);
305         }
306     }
307 
308 
309     static final boolean At = true, Before = false;
310 
311     static void testCodePoint(boolean isAt, StringBuffer sb, int index, int expected) {
312         int c = isAt ? sb.codePointAt(index) : sb.codePointBefore(index);
313 
314         check(c != expected,
315               "codePoint" + (isAt ? "At" : "Before") + "(" + index + ") for <"
316               + sb + ">", c, expected);
317     }
318 
319     static void testCodePoint(boolean isAt, StringBuffer sb, int index) {
320         boolean exceptionOccurred = false;
321 
322         try {
323             int c = isAt ? sb.codePointAt(index) : sb.codePointBefore(index);
324         }
325         catch (StringIndexOutOfBoundsException e) {
326             exceptionOccurred = true;
327         }
328         check(!exceptionOccurred,
329               "codePoint" + (isAt ? "At" : "Before") + "(" + index + ") for <"
330               + sb + "> should throw StringIndexOutOfBoundsPointerException.");
331     }
332 
333     static void testAppendCodePoint(int codePoint, Class expectedException) {
334         try {
335             new StringBuffer().appendCodePoint(codePoint);
336         } catch (Exception e) {
337             if (expectedException.isInstance(e)) {
338                 return;
339             }
340             throw new RuntimeException("Error: Unexpected exception", e);
341         }
342         check(true, "appendCodePoint(" + toHexString(codePoint) + ") didn't throw "
343               + expectedException.getName());
344     }
345 
346     static void testCodePointCount(StringBuffer sb, int beginIndex, int endIndex,
347                                    Class expectedException) {
348         try {
349             int n = sb.codePointCount(beginIndex, endIndex);
350         } catch (Exception e) {
351             if (expectedException.isInstance(e)) {
352                 return;
353             }
354             throw new RuntimeException("Error: Unexpected exception", e);
355         }
356         check(true, "codePointCount() didn't throw " + expectedException.getName());
357     }
358 
359     static void testOffsetByCodePoints(StringBuffer sb, int index, int offset,
360                                        Class expectedException) {
361         try {
362             int n = sb.offsetByCodePoints(index, offset);
363         } catch (Exception e) {
364             if (expectedException.isInstance(e)) {
365                 return;
366             }
367             throw new RuntimeException("Error: Unexpected exception", e);
368         }
369         check(true, "offsetByCodePoints() didn't throw " + expectedException.getName());
370     }
371 
372     static void check(boolean err, String msg) {
373         if (err) {
374             throw new RuntimeException("Error: " + msg);
375         }
376     }
377 
378     static void check(boolean err, String s, int got, int expected) {
379         if (err) {
380             throw new RuntimeException("Error: " + s
381                                        + " returned an unexpected value. got "
382                                        + toHexString(got)
383                                        + ", expected "
384                                        + toHexString(expected));
385         }
386     }
387 
388     static void check(boolean err, String s, StringBuffer got, String expected) {
389         if (err) {
390             throw new RuntimeException("Error: " + s
391                                        + " returned an unexpected value. got <"
392                                        + toHexString(new String(got))
393                                        + ">, expected <"
394                                        + toHexString(expected)
395                                        + ">");
396         }
397     }
398 
399     private static String toHexString(int c) {
400         return "0x" + Integer.toHexString(c);
401     }
402 
403     private static String toHexString(String s) {
404         StringBuffer sb = new StringBuffer();
405         for (int i = 0; i < s.length(); i++) {
406             char c = s.charAt(i);
407 
408             sb.append(" 0x");
409             if (c < 0x10) sb.append('0');
410             if (c < 0x100) sb.append('0');
411             if (c < 0x1000) sb.append('0');
412             sb.append(Integer.toHexString(c));
413         }
414         sb.append(' ');
415         return sb.toString();
416     }
417 }